#include "serverclient.h"

static void parse_msg(msgTokens *msg, const char *s);
static bool free_msg (msgTokens *msg);
static int log_server_data (const char *ip, const char *msg, int size);
static void* server_worker(void *threadarg);

typedef struct workerData{
	int accept_sd;
	struct sockaddr_in clientaddr;
	
} workerData;

void* start_server(void *threadarg) {
	
	struct thread_data *data; 
	data = (struct thread_data *) threadarg;

	struct workerData worker_data;
	worker_data.accept_sd = -1;
	
    int rc, on = 1;

    /*************************************************/
    /* Create an AF_INET stream socket to receive    */
    /* incoming connections on                       */
    /*************************************************/
    data->listener = socket(AF_INET, SOCK_STREAM, 0);
    if (data->listener < 0)
    {
		syslog(LOG_DEBUG, "SERVER socket() failed");
		pthread_exit(NULL); 
    }

    /*************************************************/
    /* Allow socket descriptor to be reuseable       */
    /*************************************************/
    rc = setsockopt(data->listener,
	    SOL_SOCKET,  SO_REUSEADDR,
	    (char *)&on, sizeof(on));
    if (rc < 0)
    {
		syslog(LOG_DEBUG, "SERVER setsockopt() failed");
		close(data->listener);
		pthread_exit(NULL); 
    }

    /*************************************************/
    /* Bind the socket                               */
    rc = bind(data->listener,
	    (struct sockaddr *)&data->serveraddr, sizeof(data->serveraddr));
    if (rc < 0)
    {
		syslog(LOG_DEBUG, "SERVER bind() failed");
		close(data->listener);
		pthread_exit(NULL); 
    }

    /*************************************************/
    /* Set the listen back log                       */
    /*************************************************/
    rc = listen(data->listener, 5);
    if (rc < 0)
    {
		syslog(LOG_DEBUG, "SERVER listen() failed");
		close(data->listener);
		pthread_exit(NULL); 
    }

    /*************************************************/
    /* Inform the user that the server is ready      */
    /*************************************************/
    syslog(LOG_DEBUG, "SERVER server ready");

    /*************************************************/
    /* Go through the loop once for each connection  */
    /*************************************************/
    size_t addrlen = sizeof(struct sockaddr);
	
	while(1)
    {
		/**********************************************/
		/* Wait for an incoming connection            */
		/**********************************************/
		
		syslog(LOG_DEBUG, "SERVER waiting on accept()");
		data->server_ready = true;
		
		worker_data.accept_sd = accept(data->listener, (struct sockaddr *)&worker_data.clientaddr, &addrlen);
		syslog (LOG_DEBUG, "SERVER: Found new client with ip = %s", inet_ntoa(worker_data.clientaddr.sin_addr));
		
		if (worker_data.accept_sd < 0)
		{
	    	syslog(LOG_DEBUG, "SERVER accept() failed");
			close(data->listener);
			pthread_exit(NULL); 
		}
		syslog(LOG_DEBUG, "SERVER accept() completed successfully");

		/**********************************************/
		/* Create the worker job                      */
		/**********************************************/
		syslog(LOG_DEBUG, "SERVER creating worker job");

		//Create a thread
		pthread_t thread_id;
		pthread_create(&thread_id, NULL, server_worker, (void*) &worker_data);

    }

    /*************************************************/
    /* Close down the listen socket                  */
    /*************************************************/
	close(data->listener);

}

static void* server_worker(void *threadarg) {
    
	struct workerData *orig_data; 
	orig_data = (struct workerData *) threadarg;
	
	struct workerData data;
	data.clientaddr = orig_data->clientaddr;
	data.accept_sd = orig_data->accept_sd;
	
    char recv_buf[MAX_MESSAGE_LEN];
    int rc, msg_error;
	
    /*************************************************/
    /* Receive a message from the remote party       */
    /*************************************************/

	while(1)
	{
				
		syslog (LOG_DEBUG, "SERVER WORKER Receiving: Descriptor %d", data.accept_sd);
		rc = recv(data.accept_sd, recv_buf, sizeof(recv_buf), 0);
		
		if (rc <= 0)
		{
			syslog(LOG_DEBUG, "SERVER WORKER recv_msg(): recv() failed");
			close(data.accept_sd);
			break;
		}

		msg_error = log_server_data (inet_ntoa(data.clientaddr.sin_addr), recv_buf, rc);
						
		if (msg_error == -1)
		{
			syslog (LOG_ERR, "Received invalid data from client %s", inet_ntoa(data.clientaddr.sin_addr));
		}
	
	}
	
	pthread_exit(NULL);
}

void* client_send_message (void* threaddata)
{
	int rc;
	
	struct thread_data *my_data; 
	my_data = (struct thread_data *) threaddata;
	
	syslog (LOG_DEBUG, "Client: Befor sending Descriptor: %d, Data: %s, Len data: %d",
			my_data->client_sd, my_data->data, strlen(my_data->data));
	
	rc = send(my_data->client_sd, my_data->data, strlen(my_data->data),0 );

	if(rc <= 0)
	{
		syslog(LOG_ERR, "[%lu] Client: Could not send", my_data->thread_id);
		/*@TODO: Error handling */
		pthread_exit(NULL);
	}
	/* @TODO: CHeck all characters were sent */
	else 
	{
		syslog (LOG_DEBUG, "[%lu] Client sent message successfully. Bytes sent [%d]",
				my_data->thread_id, rc);	
	}
	pthread_exit(NULL);
}

static int log_server_data (const char *ip, const char *msg, int size) {
	
	msgTokens msg_tokenized;
	int retval;
	char tmp_buf[MAX_MESSAGE_LEN];
	
	strncpy (tmp_buf, msg, size);
	
	retval = 0;
	parse_msg(&msg_tokenized, tmp_buf);
	
	syslog (LOG_DEBUG, "Checking received message: %s", tmp_buf);
	
	
	if (sizeof(msg_tokenized.tokens) == 0)
	{
		/* Error */
		retval = -1;
		goto log_error;
	}
	
	int i;
	for (i=0; i<msg_tokenized.size; i++)
	{ 	
		/* New client arrived */
		if (strcmp (CLIENT_NEW, msg_tokenized.tokens[i]) == 0 )
		{
			syslog (LOG_INFO, "New client. IP: %s", ip);
		}
		else if (strcmp (CLIENT_CHANGED_PORT, msg_tokenized.tokens[i]) == 0 )
		{
			syslog (LOG_INFO, "Client %s Port: %s", ip, msg_tokenized.tokens[i+1]);
			i++;
		}
		else
			syslog (LOG_DEBUG, "Error in token with message: %s", msg_tokenized.tokens[i]);
	}


log_error:	
	free_msg(&msg_tokenized);
	return retval;
	
}

static void parse_msg(msgTokens *msg, const char *s) {
    // Copy string to a buffer, cause strtok requires it to be writable
    int len = strlen(s);
    char buf[len + 1];
    strncpy(buf, s, len);
    buf[len] = '\0';

	int count = 0;
	
/** Determine how many tokens we've got **/
	{
		char * pch;
		pch = strtok (buf," ");		
		while (pch != NULL)
		{
			count++;
			pch = strtok (NULL, " ");
		}
	}
	
	/** Allocate and copy message into structure **/
	msg->size = count;
	msg->tokens = calloc(msg->size, sizeof(char*));
	
	{
		//Reset the buffer
		strncpy(buf, s, len);
    	buf[len] = '\0';
		char * pch;
		pch = strtok (buf," ");
		int i;
		
		for (i=0; i<msg->size; i++)
		{
			msg->tokens[i] = calloc (1, strlen(pch));
			strncpy(msg->tokens[i], pch, strlen(pch));		
			pch = strtok (NULL, " ");
			
		}
	
	}	
	
	
}

static bool free_msg (msgTokens *msg) {
	
	int i;
	
	//Free the strings
	for (i=0; i<msg->size; i++)
	{
		
		free (msg->tokens[i]);
	}
	
	free (msg->tokens);
	
	return true;
}
